#include<bits/stdc++.h>
#include"def.h"
using namespace std;
extern string filename;
string nodeTypeName[] = {
  [SYM_COMPUNIT]     = "CompUnit",
  [SYM_CONSTDECL]    = "ConstDecl",
  [SYM_VARDECL]      = "VarDecl",
  [SYM_CONSTDEF]     = "ConstDef",
  [SYM_VARDEF]       = "VarDef",
  [SYM_FUNCDEF]      = "FuncDef",
  [SYM_CONSTINITVAL] = "ConstInitVal",
  [SYM_INITVAL]      = "InitVal",
  [SYM_FUNCFPARAMS]  = "FuncFParams",
  [SYM_PARAM]        = "Param",
  [SYM_BLOCK]        = "Block",
  [SYM_BRANCH]       = "Branch",
  [SYM_IF]           = "IF",
  [SYM_ELSE]         = "ELSE",
  [SYM_WHILE]        = "While",
  [SYM_FOR]          = "For",
  [SYM_BREAK]        = "Break",
  [SYM_CONTINUE]     = "Continue",
  [SYM_RETURN]       = "Return",
  [SYM_COND]         = "Cond",
  [SYM_FUNC]         = "Func",
  [SYM_CONSTEXP]     = "ConstExp",
  [SYM_EXP]          = "Exp",
  [SYM_BRAKET]       = "[]",
  [SYM_ASSIGN]       = "=",
  [SYM_ADD]          = "+",
  [SYM_MINUS]        = "-",
  [SYM_MUL]          = "*",
  [SYM_DIV]          = "/",
  [SYM_MOD]          = "%",
  [SYM_NEGATIVE]     = "-",
  [SYM_NOT]          = "!",
  [SYM_AND]          = "&&",
  [SYM_OR]           = "||",
  [SYM_GRATER]       = ">",
  [SYM_LESS]         = "<",
  [SYM_NOTLESS]      = ">=",
  [SYM_NOTGRATER]    = "<=",
  [SYM_EQUAL]        = "==",
  [SYM_NOTEQUAL]     = "!=",
  [SYM_EMPTY]        = "",
  [SYM_LIST]         = "List",
  [SYM_INT]          = "INT",
  [SYM_FLOAT]        = "FLOAT",
  [SYM_TYPE_INT]     = "int",
  [SYM_TYPE_FLOAT]   = "float",
  [SYM_TYPE_VOID]    = "void",
  [SYM_ID]           = "ID",
  [SYM_M]            = "M",
  [SYM_N]            = "N"
};

extern int yylineno;
string notdefine="$$";

node::node(int tag,int nodeType):tag(tag),type(0),nodeType(nodeType),
lineno(yylineno),dimension(0),parent(NULL),cal_type(-1){
 type_val=&notdefine;
 myDriver=getDriver(this);
}

node::node(int tag,int nodeType,string type_val):tag(tag),type(0),nodeType(nodeType),
type_val(&type_val),lineno(yylineno),dimension(0),cal_type(-1){
 myDriver=getDriver(this);
}

string node::getName(){
  return nodeTypeName[nodeType];
}

void node::add(node* child){
  if(child->tag!=-1){
  	children.push_back(child);
  	child->parent = this;
  }
  else{
  	for(Node* i:child->children){
  		children.push_back(i);
  		child->parent = this;
  	}
  }
}

void node::analyze(){
  myDriver->calculate();
}

void node::push(initializer_list<node*> li){
  for(Node* i:li){
  	add(i);
  }
  analyze();
}
//0 name 1 int 2 float 3 type/id 4 list 5 tree
void node::output(){
  if(nodeType==SYM_M||nodeType==SYM_N)return;
  string pre = prefix==""?"":prefix+"--";
  if(type==2||type==3)pre=relativePrefix+"--";
  switch(tag){
    case-1:printf("%s%s\n"   ,pre.c_str(),nodeTypeName[nodeType].c_str());break;
    case 0:printf("%s%s\n"   ,pre.c_str(),nodeTypeName[nodeType].c_str());break;
    case 1:printf("%s%s %d\n",pre.c_str(),nodeTypeName[nodeType].c_str(),int_val);break;
    case 2:printf("%s%s %f\n",pre.c_str(),nodeTypeName[nodeType].c_str(),float_val);break;
    case 3:printf("%s%s %s\n",pre.c_str(),nodeTypeName[nodeType].c_str(),type_val->c_str());break;
    case 4:printf("%s%s"     ,pre.c_str(),nodeTypeName[nodeType].c_str());
           for(Node* i:children){
             if(i->nodeType==SYM_ID)
               printf(" %s"                ,i->type_val->c_str());
             else
               printf(" %s"                ,nodeTypeName[i->nodeType].c_str());
           }
           printf("\n");break;
    case 5:printf("%s%s",pre.c_str(),nodeTypeName[nodeType].c_str());
           if(children.size()==0)printf("\n");break;
    case 6:printf("%s%s\n",pre.c_str(),type_val->c_str());break;
  }
}


void node::display(int dep){
  if(dep == 0) prefix = "";
  string pre = prefix;
  if(type==1||type==3) pre[pre.size()-1]=' ';
  
  if(dep == 0) pre ="|";
  else pre += "  |";
  if(tag==5){
    pre=prefix;
    if(type==1||type==3) pre[pre.size()-1]=' ';
    for(int i=0;i<nodeTypeName[nodeType].size();i++)pre+=" ";
    if(children.size()==1)pre+=" |";
    else pre+="   |";
  }
  output();
  if(tag == 4)return;
  for(node* i:children){
    i->prefix=pre;
    if(i==children.back()){
    	i->type+=1;
    	i->prefix[i->prefix.size()-1]='`';
    }
    if(tag==5&&i==children.front()){
        i->type+=2;
        i->relativePrefix=children.size()==1?"":"--";
    }
    i->display(dep+1);
  }
}

nodeDriver::nodeDriver(node* drive):driver(NULL),drive(drive){
  switch(drive->nodeType){
    case SYM_COMPUNIT    :break;
    case SYM_CONSTDECL   :break;
    case SYM_VARDECL     :break;
    case SYM_CONSTDEF    :break;
    case SYM_VARDEF      :break;
    case SYM_FUNCDEF     :break;
    case SYM_CONSTINITVAL:break;
    case SYM_INITVAL     :break;
    case SYM_FUNCFPARAMS :break;
    case SYM_PARAM       :break;
    case SYM_BLOCK       :break;
    case SYM_BRANCH      :break;
    case SYM_IF          :break;
    case SYM_ELSE        :break;
    case SYM_WHILE       :break;
    case SYM_FOR         :break;
    case SYM_BREAK       :break;
    case SYM_CONTINUE    :break;
    case SYM_RETURN      :break;
    case SYM_COND        :break;
    case SYM_FUNC        :break;
    case SYM_CONSTEXP    :break;
    case SYM_EXP         :break;
    case SYM_BRAKET      :break;
    case SYM_ASSIGN      :break;
    case SYM_ADD         :driver::addcal({OP2_const});     break;
    case SYM_MINUS       :driver::addcal({OP2_const});     break;
    case SYM_MUL         :driver::addcal({OP2_const});     break;
    case SYM_DIV         :driver::addcal({OP2_const});     break;
    case SYM_MOD         :driver::addcal({OP2_const});     break;
    case SYM_NOT         :break;
    case SYM_AND         :break;
    case SYM_OR          :break;
    case SYM_GRATER      :break;
    case SYM_LESS        :break;
    case SYM_NOTLESS     :break;
    case SYM_NOTGRATER   :break;
    case SYM_EQUAL       :break;
    case SYM_NOTEQUAL    :break;
    case SYM_EMPTY       :break;
    case SYM_LIST        :break;
    case SYM_INT         :driver::addcal({VAL_const});     break;
    case SYM_FLOAT       :driver::addcal({VAL_const});     break;
    case SYM_TYPE_INT    :break;
    case SYM_TYPE_FLOAT  :break;
    case SYM_TYPE_VOID   :break;
    case SYM_ID          :break;
    case SYM_M           :break;
    case SYM_N           :break;
  }
}

node* nodeDriver::getNode(){
  return drive;
}


int VAL_const(driver* originDriver){
  nodeDriver *myDriver = (nodeDriver*)originDriver;  
  node* n = myDriver->getNode();
  n->cal_type = n->nodeType == SYM_INT?0:1;
  n->cal_int = n->int_val;
  n->cal_float = n->float_val;
  return 1;
}

int OP2_const(driver* originDriver){
  nodeDriver *myDriver = (nodeDriver*)originDriver;  
  node* n = myDriver->getNode();
  node* n1 = n->children[0];
  node* n2 = n->children[1];
  if(n1->cal_type!=-1&&n2->cal_type!=-1){
    if(n1->cal_type==1||n2->cal_type==1){
      n->cal_type = 1;
      double a = n1->cal_type == 1?n1->cal_float:n1->cal_int;
      double b = n2->cal_type == 1?n2->cal_float:n2->cal_int;
      switch(n->nodeType){
        case SYM_ADD       :n->cal_float = a + b;    break;
        case SYM_MINUS     :n->cal_float = a - b;    break;
        case SYM_MUL       :n->cal_float = a * b;    break;
        case SYM_DIV       :n->cal_float = a / b;    break;
        case SYM_MOD       :string errorLine = filename+":"+to_string(n->lineno)+" binary operator %";
                            printf("%s\n",errorLine.c_str());
                            return -1;               break;
      }
    }else{
      n->cal_type = 0;
      int a = n1->cal_int;
      int b = n2->cal_int;
      switch(n->nodeType){
        case SYM_ADD       :n->cal_int = a + b;    break;
        case SYM_MINUS     :n->cal_int = a - b;    break;
        case SYM_MUL       :n->cal_int = a * b;    break;
        case SYM_DIV       :if(b==0){
                              string errorLine = filename+":"+to_string(n->lineno)+" can't divide zero";
                              printf("%s\n",errorLine.c_str());
                              return -1;
                            }
                            n->cal_int = a / b;    break;
        case SYM_MOD       :if(b==0){
                              string errorLine = filename+":"+to_string(n->lineno)+" can't mod zero";
                              printf("%s\n",errorLine.c_str());
                              return -1;
                            }
                            n->cal_int = a % b;    break;
      }
    }
  }
  return 1;
}
